// MIT License
//
// Copyright (c) 2023-Present - Violet Hansen - (aka HotCakeX on GitHub) - Email Address: spynetgirl@outlook.com
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// See here for more information: https://github.com/HotCakeX/Harden-Windows-Security/blob/main/LICENSE
//

using System.Collections.Generic;
using System.Globalization;
using System.Runtime.InteropServices;

namespace HardenSystemSecurity.ExploitMitigation;

internal static class Main
{
	internal static unsafe int QueryImageMitigationPolicy<T>(
		string? imagePath,
		IMAGE_MITIGATION_POLICY policyType,
		uint Flags,
		ref T policy) where T : unmanaged
	{
		int size = sizeof(T); // Compile-time size for unmanaged T

		IntPtr buffer = Marshal.AllocHGlobal(size);
		try
		{
			int status = NativeMethods.RtlQueryImageMitigationPolicy(
				imagePath,
				policyType,
				Flags,
				buffer,
				(uint)size);

			if (status != 0)
			{
				return status;
			}

			// Directly read unmanaged struct from the native buffer.
			policy = *(T*)buffer;

			return status;
		}
		finally
		{
			Marshal.FreeHGlobal(buffer);
		}
	}

	internal static unsafe int SetImageMitigationPolicy<T>(
		string? imagePath,
		IMAGE_MITIGATION_POLICY policyType,
		uint Flags,
		T policy) where T : unmanaged
	{
		int size = sizeof(T); // Compile-time size for unmanaged T

		IntPtr buffer = Marshal.AllocHGlobal(size); // Allocate native memory

		try
		{
			// Copy the managed struct bits directly into the native buffer (blittable write)
			*(T*)buffer = policy;

			// Call native API with the raw buffer
			int status = NativeMethods.RtlSetImageMitigationPolicy(
				imagePath,
				policyType,
				Flags,
				buffer,
				(uint)size);

			return status;
		}
		finally
		{
			Marshal.FreeHGlobal(buffer);
		}
	}

}

/// <summary>
/// Represents the result of an operation that can either succeed or fail.
/// </summary>
/// <typeparam name="T">The type of the value when successful</typeparam>
internal sealed class Result<T>
{
	internal bool IsSuccess { get; }
	internal bool IsFailure => !IsSuccess;
	internal T Value { get; }
	internal string Error { get; }

	private Result(T value)
	{
		IsSuccess = true;
		Value = value;
		Error = string.Empty;
	}

	private Result(string error)
	{
		IsSuccess = false;
		Value = default!;
		Error = error ?? string.Empty;
	}

	internal static Result<T> Success(T value) => new(value);
	internal static Result<T> Failure(string error) => new(error);

	internal TResult Match<TResult>(Func<T, TResult> onSuccess, Func<string, TResult> onFailure) => IsSuccess ? onSuccess(Value) : onFailure(Error);

	internal void Match(Action<T> onSuccess, Action<string> onFailure)
	{
		if (IsSuccess)
			onSuccess(Value);
		else
			onFailure(Error);
	}

	internal Result<TResult> Map<TResult>(Func<T, TResult> mapper) => IsSuccess ? Result<TResult>.Success(mapper(Value)) : Result<TResult>.Failure(Error);

	internal Result<TResult> Bind<TResult>(Func<T, Result<TResult>> binder) => IsSuccess ? binder(Value) : Result<TResult>.Failure(Error);

	internal T GetValueOrDefault(T defaultValue = default!) => IsSuccess ? Value : defaultValue;

	internal T GetValueOrThrow()
	{
		if (IsFailure)
			throw new InvalidOperationException($"Result is in failure state: {Error}");
		return Value;
	}
}

/// <summary>
/// Represents the result of an operation that can either succeed or fail without returning a value.
/// </summary>
internal sealed class Result
{
	internal bool IsSuccess { get; }
	internal bool IsFailure => !IsSuccess;
	internal string Error { get; }

	private Result(bool isSuccess, string error)
	{
		IsSuccess = isSuccess;
		Error = error ?? string.Empty;
	}

	internal static Result Success() => new(true, string.Empty);
	internal static Result Failure(string error) => new(false, error);

	internal TResult Match<TResult>(Func<TResult> onSuccess, Func<string, TResult> onFailure) => IsSuccess ? onSuccess() : onFailure(Error);

	internal void Match(Action onSuccess, Action<string> onFailure)
	{
		if (IsSuccess)
			onSuccess();
		else
			onFailure(Error);
	}

	internal Result<T> Map<T>(Func<T> mapper) => IsSuccess ? Result<T>.Success(mapper()) : Result<T>.Failure(Error);

	internal Result<T> Bind<T>(Func<Result<T>> binder) => IsSuccess ? binder() : Result<T>.Failure(Error);

	internal void ThrowIfFailure()
	{
		if (IsFailure)
			throw new InvalidOperationException($"Operation failed: {Error}");
	}
}

internal sealed class FontDisablePolicy : ProcessFontDisablePolicy
{
	internal FontDisablePolicy() => forceNames.Add(ForceName.FontDisable, false);

	internal bool OverrideFontDisable
	{
		get => forceNames[ForceName.FontDisable];
		set => forceNames[ForceName.FontDisable] = value;
	}
}

internal sealed class ExtensionPointPolicy : ProcessExtensionPointPolicy
{
	internal ExtensionPointPolicy() => forceNames.Add(ForceName.ExtensionPoint, false);

	internal bool OverrideExtensionPoint
	{
		get => forceNames[ForceName.ExtensionPoint];
		set => forceNames[ForceName.ExtensionPoint] = value;
	}
}

internal sealed class HeapPolicy : Policy
{
	internal HeapPolicy()
	{
		Add(PolicyOptionName.TerminateOnError, false);
		forceNames.Add(ForceName.Heap, false);
	}

	internal OPTIONVALUE TerminateOnError
	{
		get => policyNames[PolicyOptionName.TerminateOnError];
		set => policyNames[PolicyOptionName.TerminateOnError] = value;
	}

	internal bool OverrideHeap
	{
		get => forceNames[ForceName.Heap];
		set => forceNames[ForceName.Heap] = value;
	}
}

internal sealed class ImageLoadPolicy : ProcessImageLoadPolicy
{
	internal ImageLoadPolicy()
	{
		forceNames.Add(ForceName.BlockRemoteImageLoads, false);
		forceNames.Add(ForceName.BlockLowLabel, false);
		forceNames.Add(ForceName.PreferSystem32, false);
	}

	internal bool OverrideBlockRemoteImageLoads
	{
		get => forceNames[ForceName.BlockRemoteImageLoads];
		set => forceNames[ForceName.BlockRemoteImageLoads] = value;
	}

	internal bool OverrideBlockLowLabel
	{
		get => forceNames[ForceName.BlockLowLabel];
		set => forceNames[ForceName.BlockLowLabel] = value;
	}

	internal bool OverridePreferSystem32
	{
		get => forceNames[ForceName.PreferSystem32];
		set => forceNames[ForceName.PreferSystem32] = value;
	}
}

internal sealed class PayloadPolicy : ProcessPayloadPolicy
{
	internal List<string> EAFModules;

	internal PayloadPolicy()
	{
		EAFModules = [];
		forceNames.Add(ForceName.EnableExportAddressFilter, false);
		forceNames.Add(ForceName.EnableExportAddressFilterPlus, false);
		forceNames.Add(ForceName.EnableImportAddressFilter, false);
		forceNames.Add(ForceName.EnableRopStackPivot, false);
		forceNames.Add(ForceName.EnableRopCallerCheck, false);
		forceNames.Add(ForceName.EnableRopSimExec, false);
	}

	internal bool OverrideEnableExportAddressFilter
	{
		get => forceNames[ForceName.EnableExportAddressFilter];
		set => forceNames[ForceName.EnableExportAddressFilter] = value;
	}

	internal bool OverrideEnableExportAddressFilterPlus
	{
		get => forceNames[ForceName.EnableExportAddressFilterPlus];
		set => forceNames[ForceName.EnableExportAddressFilterPlus] = value;
	}

	internal bool OverrideEnableImportAddressFilter
	{
		get => forceNames[ForceName.EnableImportAddressFilter];
		set => forceNames[ForceName.EnableImportAddressFilter] = value;
	}

	internal bool OverrideEnableRopStackPivot
	{
		get => forceNames[ForceName.EnableRopStackPivot];
		set => forceNames[ForceName.EnableRopStackPivot] = value;
	}

	internal bool OverrideEnableRopCallerCheck
	{
		get => forceNames[ForceName.EnableRopCallerCheck];
		set => forceNames[ForceName.EnableRopCallerCheck] = value;
	}

	internal bool OverrideEnableRopSimExec
	{
		get => forceNames[ForceName.EnableRopSimExec];
		set => forceNames[ForceName.EnableRopSimExec] = value;
	}
}

internal sealed class PolicyNamePair(MitigationOptions parName, PolicyName polName, PolicyOptionName optName, ForceName forceName)
{
	internal MitigationOptions ParameterName => parName;
	internal PolicyName PolicyName => polName;
	internal PolicyOptionName PolicyOptionName => optName;
	internal ForceName ForceName => forceName;
}

internal class ProcessASLRPolicy : Policy
{
	internal ProcessASLRPolicy()
	{
		Add(PolicyOptionName.ForceRelocateImages, false);
		Add(PolicyOptionName.RequireInfo, false);
		Add(PolicyOptionName.BottomUp, true);
		Add(PolicyOptionName.HighEntropy, true);
	}

	internal OPTIONVALUE ForceRelocateImages
	{
		get => policyNames[PolicyOptionName.ForceRelocateImages];
		set => policyNames[PolicyOptionName.ForceRelocateImages] = value;
	}

	internal OPTIONVALUE RequireInfo
	{
		get => policyNames[PolicyOptionName.RequireInfo];
		set => policyNames[PolicyOptionName.RequireInfo] = value;
	}

	internal OPTIONVALUE BottomUp
	{
		get => policyNames[PolicyOptionName.BottomUp];
		set => policyNames[PolicyOptionName.BottomUp] = value;
	}

	internal OPTIONVALUE HighEntropy
	{
		get => policyNames[PolicyOptionName.HighEntropy];
		set => policyNames[PolicyOptionName.HighEntropy] = value;
	}
}

internal class ProcessBinarySignaturePolicy : Policy
{
	internal ProcessBinarySignaturePolicy()
	{
		Add(PolicyOptionName.MicrosoftSignedOnly, false);
		Add(PolicyOptionName.AllowStoreSignedBinaries, false);
		Add(PolicyOptionName.Audit, false);
		Add(PolicyOptionName.AuditStoreSigned, false);
	}

	internal OPTIONVALUE MicrosoftSignedOnly
	{
		get => policyNames[PolicyOptionName.MicrosoftSignedOnly];
		set => policyNames[PolicyOptionName.MicrosoftSignedOnly] = value;
	}

	internal OPTIONVALUE AllowStoreSignedBinaries
	{
		get => policyNames[PolicyOptionName.AllowStoreSignedBinaries];
		set => policyNames[PolicyOptionName.AllowStoreSignedBinaries] = value;
	}

	internal OPTIONVALUE Audit
	{
		get => policyNames[PolicyOptionName.Audit];
		set => policyNames[PolicyOptionName.Audit] = value;
	}

	internal OPTIONVALUE AuditStoreSigned
	{
		get => policyNames[PolicyOptionName.AuditStoreSigned];
		set => policyNames[PolicyOptionName.AuditStoreSigned] = value;
	}
}

internal class ProcessCFGPolicy : Policy
{
	internal ProcessCFGPolicy()
	{
		Add(PolicyOptionName.Enable, true);
		Add(PolicyOptionName.SuppressExports, false);
		Add(PolicyOptionName.StrictControlFlowGuard, false);
	}

	internal OPTIONVALUE Enable
	{
		get => policyNames[PolicyOptionName.Enable];
		set => policyNames[PolicyOptionName.Enable] = value;
	}

	internal OPTIONVALUE SuppressExports
	{
		get => policyNames[PolicyOptionName.SuppressExports];
		set => policyNames[PolicyOptionName.SuppressExports] = value;
	}

	internal OPTIONVALUE StrictControlFlowGuard
	{
		get => policyNames[PolicyOptionName.StrictControlFlowGuard];
		set => policyNames[PolicyOptionName.StrictControlFlowGuard] = value;
	}
}

internal class ProcessChildProcessPolicy : Policy
{
	internal ProcessChildProcessPolicy()
	{
		Add(PolicyOptionName.DisallowChildProcessCreation, false);
		Add(PolicyOptionName.Audit, false);
	}

	internal OPTIONVALUE DisallowChildProcessCreation
	{
		get => policyNames[PolicyOptionName.DisallowChildProcessCreation];
		set => policyNames[PolicyOptionName.DisallowChildProcessCreation] = value;
	}

	internal OPTIONVALUE Audit
	{
		get => policyNames[PolicyOptionName.Audit];
		set => policyNames[PolicyOptionName.Audit] = value;
	}
}

internal sealed class SEHOPPolicy : ProcessSEHOPPolicy
{
	internal SEHOPPolicy()
	{
		Add(PolicyOptionName.Enable, false);
		Add(PolicyOptionName.TelemetryOnly, false);
		Add(PolicyOptionName.Audit, false);
	}

	internal bool OverrideSEHOP
	{
		get => forceNames[ForceName.SEHOP];
		set => forceNames[ForceName.SEHOP] = value;
	}
}

internal sealed class StrictHandlePolicy : ProcessStrictHandlePolicy
{
	internal StrictHandlePolicy() => forceNames.Add(ForceName.StrictHandle, false);

	internal bool OverrideStrictHandle
	{
		get => forceNames[ForceName.StrictHandle];
		set => forceNames[ForceName.StrictHandle] = value;
	}
}

internal sealed class SystemCallPolicy : ProcessSystemCallPolicy
{
	internal SystemCallPolicy()
	{
		forceNames.Add(ForceName.SystemCall, false);
		forceNames.Add(ForceName.DisableFsctlSystem, false);
	}

	internal bool OverrideFsctlSystemCall
	{
		get => forceNames[ForceName.DisableFsctlSystem];
		set => forceNames[ForceName.DisableFsctlSystem] = value;
	}

	internal bool OverrideSystemCall
	{
		get => forceNames[ForceName.SystemCall];
		set => forceNames[ForceName.SystemCall] = value;
	}
}

internal sealed class UserShadowStackPolicy : ProcessUserShadowStackPolicy
{
	internal UserShadowStackPolicy() => forceNames.Add(ForceName.UserShadowStack, false);

	internal bool OverrideUserShadowStack
	{
		get => forceNames[ForceName.UserShadowStack];
		set => forceNames[ForceName.UserShadowStack] = value;
	}
}

internal class ProcessUserShadowStackPolicy : Policy
{
	internal ProcessUserShadowStackPolicy()
	{
		Add(PolicyOptionName.UserShadowStack, false);
		Add(PolicyOptionName.UserShadowStackStrictMode, false);
		Add(PolicyOptionName.AuditUserShadowStack, false);
		Add(PolicyOptionName.SetContextIpValidation, false);
		Add(PolicyOptionName.AuditSetContextIpValidation, false);
		Add(PolicyOptionName.BlockNonCetBinaries, false);
		Add(PolicyOptionName.BlockNonCetBinariesNonEhcont, false);
		Add(PolicyOptionName.AuditBlockNonCetBinaries, false);
	}

	internal OPTIONVALUE UserShadowStack
	{
		get => policyNames[PolicyOptionName.UserShadowStack];
		set => policyNames[PolicyOptionName.UserShadowStack] = value;
	}

	internal OPTIONVALUE UserShadowStackStrictMode
	{
		get => policyNames[PolicyOptionName.UserShadowStackStrictMode];
		set => policyNames[PolicyOptionName.UserShadowStackStrictMode] = value;
	}

	internal OPTIONVALUE AuditUserShadowStack
	{
		get => policyNames[PolicyOptionName.AuditUserShadowStack];
		set => policyNames[PolicyOptionName.AuditUserShadowStack] = value;
	}

	internal OPTIONVALUE SetContextIpValidation
	{
		get => policyNames[PolicyOptionName.SetContextIpValidation];
		set => policyNames[PolicyOptionName.SetContextIpValidation] = value;
	}

	internal OPTIONVALUE AuditSetContextIpValidation
	{
		get => policyNames[PolicyOptionName.AuditSetContextIpValidation];
		set => policyNames[PolicyOptionName.AuditSetContextIpValidation] = value;
	}

	internal OPTIONVALUE BlockNonCetBinaries
	{
		get => policyNames[PolicyOptionName.BlockNonCetBinaries];
		set => policyNames[PolicyOptionName.BlockNonCetBinaries] = value;
	}

	internal OPTIONVALUE BlockNonCetBinariesNonEhcont
	{
		get => policyNames[PolicyOptionName.BlockNonCetBinariesNonEhcont];
		set => policyNames[PolicyOptionName.BlockNonCetBinariesNonEhcont] = value;
	}

	internal OPTIONVALUE AuditBlockNonCetBinaries
	{
		get => policyNames[PolicyOptionName.AuditBlockNonCetBinaries];
		set => policyNames[PolicyOptionName.AuditBlockNonCetBinaries] = value;
	}
}

internal class ProcessSystemCallPolicy : Policy
{
	internal ProcessSystemCallPolicy()
	{
		Add(PolicyOptionName.DisableWin32kSystemCalls, false);
		Add(PolicyOptionName.Audit, false);
		Add(PolicyOptionName.DisableFsctlSystemCalls, false);
		Add(PolicyOptionName.AuditFsctlSystemCalls, false);
	}

	internal OPTIONVALUE DisableFsctlSystemCalls
	{
		get => policyNames[PolicyOptionName.DisableFsctlSystemCalls];
		set => policyNames[PolicyOptionName.DisableFsctlSystemCalls] = value;
	}

	internal OPTIONVALUE AuditFsctlSystemCalls
	{
		get => policyNames[PolicyOptionName.AuditFsctlSystemCalls];
		set => policyNames[PolicyOptionName.AuditFsctlSystemCalls] = value;
	}

	internal OPTIONVALUE DisableWin32kSystemCalls
	{
		get => policyNames[PolicyOptionName.DisableWin32kSystemCalls];
		set => policyNames[PolicyOptionName.DisableWin32kSystemCalls] = value;
	}

	internal OPTIONVALUE Audit
	{
		get => policyNames[PolicyOptionName.Audit];
		set => policyNames[PolicyOptionName.Audit] = value;
	}
}

internal class ProcessStrictHandlePolicy : Policy
{
	internal ProcessStrictHandlePolicy() => Add(PolicyOptionName.Enable, false);

	internal OPTIONVALUE Enable
	{
		get => policyNames[PolicyOptionName.Enable];
		set => policyNames[PolicyOptionName.Enable] = value;
	}
}

internal class ProcessSEHOPPolicy : Policy
{
	internal ProcessSEHOPPolicy() => forceNames.Add(ForceName.SEHOP, false);

	internal OPTIONVALUE Enable
	{
		get => policyNames[PolicyOptionName.Enable];
		set => policyNames[PolicyOptionName.Enable] = value;
	}

	internal OPTIONVALUE TelemetryOnly
	{
		get => policyNames[PolicyOptionName.TelemetryOnly];
		set => policyNames[PolicyOptionName.TelemetryOnly] = value;
	}

	internal OPTIONVALUE Audit
	{
		get => policyNames[PolicyOptionName.Audit];
		set => policyNames[PolicyOptionName.Audit] = value;
	}
}

internal class ProcessPayloadPolicy : Policy
{
	internal ProcessPayloadPolicy()
	{
		Add(PolicyOptionName.EnableExportAddressFilter, false);
		Add(PolicyOptionName.AuditEnableExportAddressFilter, false);
		Add(PolicyOptionName.EnableExportAddressFilterPlus, false);
		Add(PolicyOptionName.AuditEnableExportAddressFilterPlus, false);
		Add(PolicyOptionName.EnableImportAddressFilter, false);
		Add(PolicyOptionName.AuditEnableImportAddressFilter, false);
		Add(PolicyOptionName.EnableRopStackPivot, false);
		Add(PolicyOptionName.AuditEnableRopStackPivot, false);
		Add(PolicyOptionName.EnableRopCallerCheck, false);
		Add(PolicyOptionName.AuditEnableRopCallerCheck, false);
		Add(PolicyOptionName.EnableRopSimExec, false);
		Add(PolicyOptionName.AuditEnableRopSimExec, false);
	}

	internal OPTIONVALUE EnableExportAddressFilter
	{
		get => policyNames[PolicyOptionName.EnableExportAddressFilter];
		set => policyNames[PolicyOptionName.EnableExportAddressFilter] = value;
	}

	internal OPTIONVALUE AuditEnableExportAddressFilter
	{
		get => policyNames[PolicyOptionName.AuditEnableExportAddressFilter];
		set => policyNames[PolicyOptionName.AuditEnableExportAddressFilter] = value;
	}

	internal OPTIONVALUE EnableExportAddressFilterPlus
	{
		get => policyNames[PolicyOptionName.EnableExportAddressFilterPlus];
		set => policyNames[PolicyOptionName.EnableExportAddressFilterPlus] = value;
	}

	internal OPTIONVALUE AuditEnableExportAddressFilterPlus
	{
		get => policyNames[PolicyOptionName.AuditEnableExportAddressFilterPlus];
		set => policyNames[PolicyOptionName.AuditEnableExportAddressFilterPlus] = value;
	}

	internal OPTIONVALUE EnableImportAddressFilter
	{
		get => policyNames[PolicyOptionName.EnableImportAddressFilter];
		set => policyNames[PolicyOptionName.EnableImportAddressFilter] = value;
	}

	internal OPTIONVALUE AuditEnableImportAddressFilter
	{
		get => policyNames[PolicyOptionName.AuditEnableImportAddressFilter];
		set => policyNames[PolicyOptionName.AuditEnableImportAddressFilter] = value;
	}

	internal OPTIONVALUE EnableRopStackPivot
	{
		get => policyNames[PolicyOptionName.EnableRopStackPivot];
		set => policyNames[PolicyOptionName.EnableRopStackPivot] = value;
	}

	internal OPTIONVALUE AuditEnableRopStackPivot
	{
		get => policyNames[PolicyOptionName.AuditEnableRopStackPivot];
		set => policyNames[PolicyOptionName.AuditEnableRopStackPivot] = value;
	}

	internal OPTIONVALUE EnableRopCallerCheck
	{
		get => policyNames[PolicyOptionName.EnableRopCallerCheck];
		set => policyNames[PolicyOptionName.EnableRopCallerCheck] = value;
	}

	internal OPTIONVALUE AuditEnableRopCallerCheck
	{
		get => policyNames[PolicyOptionName.AuditEnableRopCallerCheck];
		set => policyNames[PolicyOptionName.AuditEnableRopCallerCheck] = value;
	}

	internal OPTIONVALUE EnableRopSimExec
	{
		get => policyNames[PolicyOptionName.EnableRopSimExec];
		set => policyNames[PolicyOptionName.EnableRopSimExec] = value;
	}

	internal OPTIONVALUE AuditEnableRopSimExec
	{
		get => policyNames[PolicyOptionName.AuditEnableRopSimExec];
		set => policyNames[PolicyOptionName.AuditEnableRopSimExec] = value;
	}
}

internal sealed class CFGPolicy : ProcessCFGPolicy
{
	internal CFGPolicy()
	{
		forceNames.Add(ForceName.CFG, false);
		forceNames.Add(ForceName.StrictCFG, false);
	}

	internal bool OverrideCFG
	{
		get => forceNames[ForceName.CFG];
		set => forceNames[ForceName.CFG] = value;
	}

	internal bool OverrideStrictCFG
	{
		get => forceNames[ForceName.StrictCFG];
		set => forceNames[ForceName.StrictCFG] = value;
	}
}

internal sealed class ASLRPolicy : ProcessASLRPolicy
{
	internal ASLRPolicy()
	{
		forceNames.Add(ForceName.ForceRelocateImages, false);
		forceNames.Add(ForceName.BottomUp, false);
		forceNames.Add(ForceName.HighEntropy, false);
	}

	internal bool OverrideForceRelocateImages
	{
		get => forceNames[ForceName.ForceRelocateImages];
		set => forceNames[ForceName.ForceRelocateImages] = value;
	}

	internal bool OverrideBottomUp
	{
		get => forceNames[ForceName.BottomUp];
		set => forceNames[ForceName.BottomUp] = value;
	}

	internal bool OverrideHighEntropy
	{
		get => forceNames[ForceName.HighEntropy];
		set => forceNames[ForceName.HighEntropy] = value;
	}
}

internal sealed class ChildProcessPolicy : ProcessChildProcessPolicy
{
	internal ChildProcessPolicy() => forceNames.Add(ForceName.ChildProcess, false);

	internal bool OverrideChildProcess
	{
		get => forceNames[ForceName.ChildProcess];
		set => forceNames[ForceName.ChildProcess] = value;
	}
}

internal sealed class DynamicCodePolicy : ProcessDynamicCodePolicy
{
	internal DynamicCodePolicy() => forceNames.Add(ForceName.DynamicCode, false);

	internal bool OverrideDynamicCode
	{
		get => forceNames[ForceName.DynamicCode];
		set => forceNames[ForceName.DynamicCode] = value;
	}
}

internal class ProcessDEPPolicy : Policy
{
	internal ProcessDEPPolicy()
	{
		Add(PolicyOptionName.Enable, true);
		Add(PolicyOptionName.EmulateAtlThunks, false);
	}

	internal OPTIONVALUE Enable
	{
		get => policyNames[PolicyOptionName.Enable];
		set => policyNames[PolicyOptionName.Enable] = value;
	}

	internal OPTIONVALUE EmulateAtlThunks
	{
		get => policyNames[PolicyOptionName.EmulateAtlThunks];
		set => policyNames[PolicyOptionName.EmulateAtlThunks] = value;
	}
}

internal class ProcessExtensionPointPolicy : Policy
{
	internal ProcessExtensionPointPolicy() => Add(PolicyOptionName.DisableExtensionPoints, false);

	internal OPTIONVALUE DisableExtensionPoints
	{
		get => policyNames[PolicyOptionName.DisableExtensionPoints];
		set => policyNames[PolicyOptionName.DisableExtensionPoints] = value;
	}
}

internal class ProcessFontDisablePolicy : Policy
{
	internal ProcessFontDisablePolicy()
	{
		Add(PolicyOptionName.DisableNonSystemFonts, false);
		Add(PolicyOptionName.Audit, false);
	}

	internal OPTIONVALUE DisableNonSystemFonts
	{
		get => policyNames[PolicyOptionName.DisableNonSystemFonts];
		set => policyNames[PolicyOptionName.DisableNonSystemFonts] = value;
	}

	internal OPTIONVALUE Audit
	{
		get => policyNames[PolicyOptionName.Audit];
		set => policyNames[PolicyOptionName.Audit] = value;
	}
}

internal class ProcessImageLoadPolicy : Policy
{
	internal ProcessImageLoadPolicy()
	{
		Add(PolicyOptionName.BlockRemoteImageLoads, false);
		Add(PolicyOptionName.BlockLowLabelImageLoads, false);
		Add(PolicyOptionName.PreferSystem32, false);
		Add(PolicyOptionName.AuditRemoteImageLoads, false);
		Add(PolicyOptionName.AuditLowLabelImageLoads, false);
		Add(PolicyOptionName.AuditPreferSystem32, false);
	}

	internal OPTIONVALUE BlockRemoteImageLoads
	{
		get => policyNames[PolicyOptionName.BlockRemoteImageLoads];
		set => policyNames[PolicyOptionName.BlockRemoteImageLoads] = value;
	}

	internal OPTIONVALUE BlockLowLabelImageLoads
	{
		get => policyNames[PolicyOptionName.BlockLowLabelImageLoads];
		set => policyNames[PolicyOptionName.BlockLowLabelImageLoads] = value;
	}

	internal OPTIONVALUE PreferSystem32
	{
		get => policyNames[PolicyOptionName.PreferSystem32];
		set => policyNames[PolicyOptionName.PreferSystem32] = value;
	}

	internal OPTIONVALUE AuditRemoteImageLoads
	{
		get => policyNames[PolicyOptionName.AuditRemoteImageLoads];
		set => policyNames[PolicyOptionName.AuditRemoteImageLoads] = value;
	}

	internal OPTIONVALUE AuditLowLabelImageLoads
	{
		get => policyNames[PolicyOptionName.AuditLowLabelImageLoads];
		set => policyNames[PolicyOptionName.AuditLowLabelImageLoads] = value;
	}

	internal OPTIONVALUE AuditPreferSystem32
	{
		get => policyNames[PolicyOptionName.AuditPreferSystem32];
		set => policyNames[PolicyOptionName.AuditPreferSystem32] = value;
	}
}

internal sealed class AppMitigations
{
	internal string ProcessName;
	internal string Source;
	internal Dictionary<PolicyName, Policy> Policies;

	internal AppMitigations(AppMitigations appMitigations)
	  : this(appMitigations.ProcessName, appMitigations) => Source = appMitigations.Source;

	internal AppMitigations(string? name, AppMitigations defaultMitigations) : this(name)
	{
		if (defaultMitigations is null)
			return;
		foreach (KeyValuePair<PolicyName, Policy> policy in Policies)
		{
			foreach (PolicyOptionName allOption in policy.Value.GetAllOptions())
				Policies[policy.Key].SetPolicy(allOption, defaultMitigations.Policies[policy.Key].GetPolicy(allOption));
		}
	}

	internal AppMitigations(string? name)
	{
		Policies = [];
		ProcessName = name ?? "unknown";
		Source = "None";
		Policies.Add(PolicyName.DEP, new DEPPolicy());
		Policies.Add(PolicyName.ASLR, new ASLRPolicy());
		Policies.Add(PolicyName.StrictHandle, new StrictHandlePolicy());
		Policies.Add(PolicyName.SystemCalls, new SystemCallPolicy());
		Policies.Add(PolicyName.ExtensionPoints, new ExtensionPointPolicy());
		Policies.Add(PolicyName.DynamicCode, new DynamicCodePolicy());
		Policies.Add(PolicyName.ControlFlowGuard, new CFGPolicy());
		Policies.Add(PolicyName.SignedBinaries, new BinarySignaturePolicy());
		Policies.Add(PolicyName.Fonts, new FontDisablePolicy());
		Policies.Add(PolicyName.ImageLoad, new ImageLoadPolicy());
		Policies.Add(PolicyName.Payload, new PayloadPolicy());
		Policies.Add(PolicyName.SEHOP, new SEHOPPolicy());
		Policies.Add(PolicyName.Heap, new HeapPolicy());
		Policies.Add(PolicyName.ChildProcess, new ChildProcessPolicy());
		Policies.Add(PolicyName.UserShadowStack, new UserShadowStackPolicy());
	}

	internal void SetPolicy(PolicyName name, Policy policy)
	{
		if (!Policies.TryGetValue(name, out Policy? value))
			return;
		foreach (PolicyOptionName allOption in value.GetAllOptions())
			value.SetPolicy(allOption, policy.GetPolicy(allOption));
	}

	internal DEPPolicy Dep => (DEPPolicy)Policies[PolicyName.DEP];

	internal ASLRPolicy Aslr => (ASLRPolicy)Policies[PolicyName.ASLR];

	internal StrictHandlePolicy StrictHandle => (StrictHandlePolicy)Policies[PolicyName.StrictHandle];

	internal SystemCallPolicy SystemCall => (SystemCallPolicy)Policies[PolicyName.SystemCalls];

	internal ExtensionPointPolicy ExtensionPoint => (ExtensionPointPolicy)Policies[PolicyName.ExtensionPoints];

	internal DynamicCodePolicy DynamicCode => (DynamicCodePolicy)Policies[PolicyName.DynamicCode];

	internal CFGPolicy Cfg => (CFGPolicy)Policies[PolicyName.ControlFlowGuard];

	internal BinarySignaturePolicy BinarySignature => (BinarySignaturePolicy)Policies[PolicyName.SignedBinaries];

	internal FontDisablePolicy FontDisable => (FontDisablePolicy)Policies[PolicyName.Fonts];

	internal ImageLoadPolicy ImageLoad => (ImageLoadPolicy)Policies[PolicyName.ImageLoad];

	internal PayloadPolicy Payload => (PayloadPolicy)Policies[PolicyName.Payload];

	internal ChildProcessPolicy ChildProcess => (ChildProcessPolicy)Policies[PolicyName.ChildProcess];

	internal UserShadowStackPolicy UserShadowStack => (UserShadowStackPolicy)Policies[PolicyName.UserShadowStack];

	internal SEHOPPolicy Sehop => (SEHOPPolicy)Policies[PolicyName.SEHOP];

	internal HeapPolicy Heap => (HeapPolicy)Policies[PolicyName.Heap];
}

internal sealed class DEPPolicy : ProcessDEPPolicy
{
	internal DEPPolicy() => forceNames.Add(ForceName.DEP, false);

	internal bool OverrideDEP
	{
		get => forceNames[ForceName.DEP];
		set => forceNames[ForceName.DEP] = value;
	}
}

internal class Policy
{
	internal uint Flags;
	protected Dictionary<PolicyOptionName, OPTIONVALUE> policyNames;
	protected Dictionary<ForceName, bool> forceNames;
	protected Dictionary<PolicyOptionName, bool> defaults;

	internal static OPTIONVALUE BoolToOptionValue(bool input) => !input ? OPTIONVALUE.OFF : OPTIONVALUE.ON;

	internal static bool IsOptionConfigured(OPTIONVALUE input) => input is OPTIONVALUE.ON or OPTIONVALUE.OFF;

	internal static bool IsOptionOn(OPTIONVALUE input) => input is OPTIONVALUE.ON;

	internal Policy()
	{
		Flags = 0U;
		policyNames = [];
		forceNames = [];
		defaults = [];
	}

	internal void SetPolicy(PolicyOptionName name, OPTIONVALUE value) => policyNames[name] = value;

	internal OPTIONVALUE GetPolicy(PolicyOptionName name) => policyNames.TryGetValue(name, out OPTIONVALUE value) ? value : OPTIONVALUE.NOTSET;

	internal bool HasPolicy(PolicyOptionName name) => policyNames.ContainsKey(name);

	internal List<PolicyOptionName> GetAllOptions()
	{
		List<PolicyOptionName> allOptions = [];
		foreach (KeyValuePair<PolicyOptionName, OPTIONVALUE> policyName in policyNames)
			allOptions.Add(policyName.Key);
		return allOptions;
	}

	internal List<ForceName> GetAllForceOptions()
	{
		List<ForceName> allForceOptions = [];
		foreach (KeyValuePair<ForceName, bool> forceName in forceNames)
			allForceOptions.Add(forceName.Key);
		return allForceOptions;
	}

	internal bool IsForced(ForceName name) => forceNames.ContainsKey(name) && forceNames[name];

	internal void SetForce(ForceName name, bool value) => forceNames[name] = value;

	internal void SetDefaultValue(PolicyOptionName name, bool value) => defaults[name] = value;

	internal bool GetDefaultValue(PolicyOptionName name) => defaults[name];

	protected void Add(PolicyOptionName name, bool defaultValue)
	{
		policyNames.Add(name, OPTIONVALUE.NOTSET);
		defaults.Add(name, defaultValue);
	}
}

internal sealed class PolicyPairList
{
	internal static List<PolicyNamePair> policyPairs = [];

	static PolicyPairList()
	{
		policyPairs.Add(new PolicyNamePair(MitigationOptions.DEP, PolicyName.DEP, PolicyOptionName.Enable, ForceName.DEP));
		policyPairs.Add(new PolicyNamePair(MitigationOptions.EmulateAtlThunks, PolicyName.DEP, PolicyOptionName.EmulateAtlThunks, ForceName.DEP));
		policyPairs.Add(new PolicyNamePair(MitigationOptions.ForceRelocateImages, PolicyName.ASLR, PolicyOptionName.ForceRelocateImages, ForceName.ForceRelocateImages));
		policyPairs.Add(new PolicyNamePair(MitigationOptions.RequireInfo, PolicyName.ASLR, PolicyOptionName.RequireInfo, ForceName.ForceRelocateImages));
		policyPairs.Add(new PolicyNamePair(MitigationOptions.BottomUp, PolicyName.ASLR, PolicyOptionName.BottomUp, ForceName.BottomUp));
		policyPairs.Add(new PolicyNamePair(MitigationOptions.HighEntropy, PolicyName.ASLR, PolicyOptionName.HighEntropy, ForceName.HighEntropy));
		policyPairs.Add(new PolicyNamePair(MitigationOptions.StrictHandle, PolicyName.StrictHandle, PolicyOptionName.Enable, ForceName.StrictHandle));
		policyPairs.Add(new PolicyNamePair(MitigationOptions.DisableWin32kSystemCalls, PolicyName.SystemCalls, PolicyOptionName.DisableWin32kSystemCalls, ForceName.SystemCall));
		policyPairs.Add(new PolicyNamePair(MitigationOptions.AuditSystemCall, PolicyName.SystemCalls, PolicyOptionName.Audit, ForceName.SystemCall));
		policyPairs.Add(new PolicyNamePair(MitigationOptions.DisableFsctlSystemCalls, PolicyName.SystemCalls, PolicyOptionName.DisableFsctlSystemCalls, ForceName.DisableFsctlSystem));
		policyPairs.Add(new PolicyNamePair(MitigationOptions.AuditFsctlSystemCall, PolicyName.SystemCalls, PolicyOptionName.AuditFsctlSystemCalls, ForceName.DisableFsctlSystem));
		policyPairs.Add(new PolicyNamePair(MitigationOptions.DisableExtensionPoints, PolicyName.ExtensionPoints, PolicyOptionName.DisableExtensionPoints, ForceName.ExtensionPoint));
		policyPairs.Add(new PolicyNamePair(MitigationOptions.BlockDynamicCode, PolicyName.DynamicCode, PolicyOptionName.BlockDynamicCode, ForceName.DynamicCode));
		policyPairs.Add(new PolicyNamePair(MitigationOptions.AllowThreadsToOptOut, PolicyName.DynamicCode, PolicyOptionName.AllowThreadsToOptOut, ForceName.DynamicCode));
		policyPairs.Add(new PolicyNamePair(MitigationOptions.AuditDynamicCode, PolicyName.DynamicCode, PolicyOptionName.Audit, ForceName.DynamicCode));
		policyPairs.Add(new PolicyNamePair(MitigationOptions.CFG, PolicyName.ControlFlowGuard, PolicyOptionName.Enable, ForceName.CFG));
		policyPairs.Add(new PolicyNamePair(MitigationOptions.SuppressExports, PolicyName.ControlFlowGuard, PolicyOptionName.SuppressExports, ForceName.CFG));
		policyPairs.Add(new PolicyNamePair(MitigationOptions.StrictCFG, PolicyName.ControlFlowGuard, PolicyOptionName.StrictControlFlowGuard, ForceName.StrictCFG));
		policyPairs.Add(new PolicyNamePair(MitigationOptions.MicrosoftSignedOnly, PolicyName.SignedBinaries, PolicyOptionName.MicrosoftSignedOnly, ForceName.MicrosoftSignedOnly));
		policyPairs.Add(new PolicyNamePair(MitigationOptions.AllowStoreSignedBinaries, PolicyName.SignedBinaries, PolicyOptionName.AllowStoreSignedBinaries, ForceName.MicrosoftSignedOnly));
		policyPairs.Add(new PolicyNamePair(MitigationOptions.EnforceModuleDependencySigning, PolicyName.SignedBinaries, PolicyOptionName.EnforceModuleDependencySigning, ForceName.EnforceModuleDependencySigning));
		policyPairs.Add(new PolicyNamePair(MitigationOptions.AuditMicrosoftSigned, PolicyName.SignedBinaries, PolicyOptionName.Audit, ForceName.MicrosoftSignedOnly));
		policyPairs.Add(new PolicyNamePair(MitigationOptions.AuditStoreSigned, PolicyName.SignedBinaries, PolicyOptionName.AuditStoreSigned, ForceName.MicrosoftSignedOnly));
		policyPairs.Add(new PolicyNamePair(MitigationOptions.DisableNonSystemFonts, PolicyName.Fonts, PolicyOptionName.DisableNonSystemFonts, ForceName.FontDisable));
		policyPairs.Add(new PolicyNamePair(MitigationOptions.AuditFont, PolicyName.Fonts, PolicyOptionName.Audit, ForceName.FontDisable));
		policyPairs.Add(new PolicyNamePair(MitigationOptions.BlockRemoteImageLoads, PolicyName.ImageLoad, PolicyOptionName.BlockRemoteImageLoads, ForceName.BlockRemoteImageLoads));
		policyPairs.Add(new PolicyNamePair(MitigationOptions.BlockLowLabelImageLoads, PolicyName.ImageLoad, PolicyOptionName.BlockLowLabelImageLoads, ForceName.BlockLowLabel));
		policyPairs.Add(new PolicyNamePair(MitigationOptions.PreferSystem32, PolicyName.ImageLoad, PolicyOptionName.PreferSystem32, ForceName.PreferSystem32));
		policyPairs.Add(new PolicyNamePair(MitigationOptions.AuditRemoteImageLoads, PolicyName.ImageLoad, PolicyOptionName.AuditRemoteImageLoads, ForceName.BlockRemoteImageLoads));
		policyPairs.Add(new PolicyNamePair(MitigationOptions.AuditLowLabelImageLoads, PolicyName.ImageLoad, PolicyOptionName.AuditLowLabelImageLoads, ForceName.BlockLowLabel));
		policyPairs.Add(new PolicyNamePair(MitigationOptions.AuditPreferSystem32, PolicyName.ImageLoad, PolicyOptionName.AuditPreferSystem32, ForceName.PreferSystem32));
		policyPairs.Add(new PolicyNamePair(MitigationOptions.EnableExportAddressFilter, PolicyName.Payload, PolicyOptionName.EnableExportAddressFilter, ForceName.EnableExportAddressFilter));
		policyPairs.Add(new PolicyNamePair(MitigationOptions.AuditEnableExportAddressFilter, PolicyName.Payload, PolicyOptionName.AuditEnableExportAddressFilter, ForceName.EnableExportAddressFilter));
		policyPairs.Add(new PolicyNamePair(MitigationOptions.EnableExportAddressFilterPlus, PolicyName.Payload, PolicyOptionName.EnableExportAddressFilterPlus, ForceName.EnableExportAddressFilterPlus));
		policyPairs.Add(new PolicyNamePair(MitigationOptions.AuditEnableExportAddressFilterPlus, PolicyName.Payload, PolicyOptionName.AuditEnableExportAddressFilterPlus, ForceName.EnableExportAddressFilterPlus));
		policyPairs.Add(new PolicyNamePair(MitigationOptions.EnableImportAddressFilter, PolicyName.Payload, PolicyOptionName.EnableImportAddressFilter, ForceName.EnableImportAddressFilter));
		policyPairs.Add(new PolicyNamePair(MitigationOptions.AuditEnableImportAddressFilter, PolicyName.Payload, PolicyOptionName.AuditEnableImportAddressFilter, ForceName.EnableImportAddressFilter));
		policyPairs.Add(new PolicyNamePair(MitigationOptions.EnableRopStackPivot, PolicyName.Payload, PolicyOptionName.EnableRopStackPivot, ForceName.EnableRopStackPivot));
		policyPairs.Add(new PolicyNamePair(MitigationOptions.AuditEnableRopStackPivot, PolicyName.Payload, PolicyOptionName.AuditEnableRopStackPivot, ForceName.EnableRopStackPivot));
		policyPairs.Add(new PolicyNamePair(MitigationOptions.EnableRopCallerCheck, PolicyName.Payload, PolicyOptionName.EnableRopCallerCheck, ForceName.EnableRopCallerCheck));
		policyPairs.Add(new PolicyNamePair(MitigationOptions.AuditEnableRopCallerCheck, PolicyName.Payload, PolicyOptionName.AuditEnableRopCallerCheck, ForceName.EnableRopCallerCheck));
		policyPairs.Add(new PolicyNamePair(MitigationOptions.EnableRopSimExec, PolicyName.Payload, PolicyOptionName.EnableRopSimExec, ForceName.EnableRopSimExec));
		policyPairs.Add(new PolicyNamePair(MitigationOptions.AuditEnableRopSimExec, PolicyName.Payload, PolicyOptionName.AuditEnableRopSimExec, ForceName.EnableRopSimExec));
		policyPairs.Add(new PolicyNamePair(MitigationOptions.SEHOP, PolicyName.SEHOP, PolicyOptionName.Enable, ForceName.SEHOP));
		policyPairs.Add(new PolicyNamePair(MitigationOptions.AuditSEHOP, PolicyName.SEHOP, PolicyOptionName.Audit, ForceName.SEHOP));
		policyPairs.Add(new PolicyNamePair(MitigationOptions.SEHOPTelemetry, PolicyName.SEHOP, PolicyOptionName.TelemetryOnly, ForceName.SEHOP));
		policyPairs.Add(new PolicyNamePair(MitigationOptions.TerminateOnError, PolicyName.Heap, PolicyOptionName.TerminateOnError, ForceName.Heap));
		policyPairs.Add(new PolicyNamePair(MitigationOptions.DisallowChildProcessCreation, PolicyName.ChildProcess, PolicyOptionName.DisallowChildProcessCreation, ForceName.ChildProcess));
		policyPairs.Add(new PolicyNamePair(MitigationOptions.AuditChildProcess, PolicyName.ChildProcess, PolicyOptionName.Audit, ForceName.ChildProcess));
		policyPairs.Add(new PolicyNamePair(MitigationOptions.UserShadowStack, PolicyName.UserShadowStack, PolicyOptionName.UserShadowStack, ForceName.UserShadowStack));
		policyPairs.Add(new PolicyNamePair(MitigationOptions.UserShadowStackStrictMode, PolicyName.UserShadowStack, PolicyOptionName.UserShadowStackStrictMode, ForceName.UserShadowStack));
		policyPairs.Add(new PolicyNamePair(MitigationOptions.AuditUserShadowStack, PolicyName.UserShadowStack, PolicyOptionName.AuditUserShadowStack, ForceName.UserShadowStack));
	}

	internal static List<MitigationOptions> GetParameterList()
	{
		List<MitigationOptions> parameterList = [];
		foreach (PolicyNamePair policyPair in policyPairs)
			parameterList.Add(policyPair.ParameterName);
		return parameterList;
	}

	internal static int GetCount() => policyPairs.Count;

	internal static PolicyNamePair GetPairById(int id) => policyPairs[id];
}

internal class ProcessDynamicCodePolicy : Policy
{
	internal ProcessDynamicCodePolicy()
	{
		Add(PolicyOptionName.BlockDynamicCode, false);
		Add(PolicyOptionName.AllowThreadsToOptOut, false);
		Add(PolicyOptionName.Audit, false);
	}

	internal OPTIONVALUE BlockDynamicCode
	{
		get => policyNames[PolicyOptionName.BlockDynamicCode];
		set => policyNames[PolicyOptionName.BlockDynamicCode] = value;
	}

	internal OPTIONVALUE AllowThreadsToOptOut
	{
		get => policyNames[PolicyOptionName.AllowThreadsToOptOut];
		set => policyNames[PolicyOptionName.AllowThreadsToOptOut] = value;
	}

	internal OPTIONVALUE Audit
	{
		get => policyNames[PolicyOptionName.Audit];
		set => policyNames[PolicyOptionName.Audit] = value;
	}
}

internal sealed class BinarySignaturePolicy : ProcessBinarySignaturePolicy
{
	internal BinarySignaturePolicy()
	{
		Add(PolicyOptionName.EnforceModuleDependencySigning, false);
		Add(PolicyOptionName.AuditEnforceModuleDependencySigning, false);
		forceNames.Add(ForceName.MicrosoftSignedOnly, false);
		forceNames.Add(ForceName.EnforceModuleDependencySigning, false);
	}

	internal OPTIONVALUE EnforceModuleDependencySigning
	{
		get => policyNames[PolicyOptionName.EnforceModuleDependencySigning];
		set => policyNames[PolicyOptionName.EnforceModuleDependencySigning] = value;
	}

	internal OPTIONVALUE AuditEnforceModuleDependencySigning
	{
		get => policyNames[PolicyOptionName.AuditEnforceModuleDependencySigning];
		set => policyNames[PolicyOptionName.AuditEnforceModuleDependencySigning] = value;
	}

	internal bool OverrideMicrosoftSignedOnly
	{
		get => forceNames[ForceName.MicrosoftSignedOnly];
		set => forceNames[ForceName.MicrosoftSignedOnly] = value;
	}

	internal bool OverrideEnforceModuleDependencySigning
	{
		get => forceNames[ForceName.EnforceModuleDependencySigning];
		set => forceNames[ForceName.EnforceModuleDependencySigning] = value;
	}
}


internal enum OPTIONVALUE
{
	NOTSET = 0,
	ON = 1, // Using on will prevent this value from changing in Microsoft Defender GUI.
	OFF = 2,
	ERROR = 4,
}

internal enum PROCESS_MITIGATION_POLICY
{
	ProcessDEPPolicy,
	ProcessASLRPolicy,
	ProcessDynamicCodePolicy,
	ProcessStrictHandleCheckPolicy,
	ProcessSystemCallDisablePolicy,
	ProcessMitigationOptionsMask,
	ProcessExtensionPointDisablePolicy,
	ProcessControlFlowGuardPolicy,
	ProcessSignaturePolicy,
	ProcessFontDisablePolicy,
	ProcessImageLoadPolicy,
	ProcessSystemCallFilterPolicy,
	ProcessPayloadRestrictionPolicy,
	ProcessChildProcessPolicy,
	ProcessSideChannelIsolationPolicy,
	ProcessUserShadowStackPolicy,
	ProcessRedirectionTrustPolicy,
	ProcessUserPointerAuthPolicy,
	ProcessSEHOPPolicy,
	MaxProcessMitigationPolicy,
}

internal enum PROCESS_MITIGATION_FLAGS
{
	FlagNone = 0,
	FlagReset = 1,
	FlagRemove = 2,
	FlagOSDefault = 4,
	FlagAudit = 8,
}

// All of the valid mitigation options that can be used for enabling or disabling.
internal enum MitigationOptions : uint
{
	DEP = 0U,
	EmulateAtlThunks = 1U,
	ForceRelocateImages = 2U,
	RequireInfo = 3U,
	BottomUp = 4U,
	HighEntropy = 5U,
	StrictHandle = 6U,
	DisableWin32kSystemCalls = 7U,
	AuditSystemCall = 8U,
	DisableExtensionPoints = 9U,
	DisableFsctlSystemCalls = 10U,
	AuditFsctlSystemCall = 11U,
	BlockDynamicCode = 12U,
	AllowThreadsToOptOut = 13U,
	AuditDynamicCode = 14U,
	CFG = 15U,
	SuppressExports = 16U,
	StrictCFG = 17U,
	MicrosoftSignedOnly = 18U,
	AllowStoreSignedBinaries = 19U,
	AuditMicrosoftSigned = 20U,
	AuditStoreSigned = 21U,
	EnforceModuleDependencySigning = 22U,
	DisableNonSystemFonts = 23U,
	AuditFont = 24U,
	BlockRemoteImageLoads = 25U,
	BlockLowLabelImageLoads = 26U,
	PreferSystem32 = 27U,
	AuditRemoteImageLoads = 28U,
	AuditLowLabelImageLoads = 29U,
	AuditPreferSystem32 = 30U,
	EnableExportAddressFilter = 31U,
	AuditEnableExportAddressFilter = 32U,
	EnableExportAddressFilterPlus = 33U,
	AuditEnableExportAddressFilterPlus = 34U,
	EnableImportAddressFilter = 35U,
	AuditEnableImportAddressFilter = 36U,
	EnableRopStackPivot = 37U,
	AuditEnableRopStackPivot = 38U,
	EnableRopCallerCheck = 39U,
	AuditEnableRopCallerCheck = 40U,
	EnableRopSimExec = 41U,
	AuditEnableRopSimExec = 42U,
	SEHOP = 43U,
	AuditSEHOP = 44U,
	SEHOPTelemetry = 45U,
	TerminateOnError = 46U,
	DisallowChildProcessCreation = 47U,
	AuditChildProcess = 48U,
	UserShadowStack = 49U,
	UserShadowStackStrictMode = 50U,
	AuditUserShadowStack = 51U
}

internal enum PolicyName
{
	DEP,
	ASLR,
	StrictHandle,
	SystemCalls,
	ExtensionPoints,
	DynamicCode,
	ControlFlowGuard,
	SignedBinaries,
	Fonts,
	ImageLoad,
	Payload,
	ChildProcess,
	UserShadowStack,
	SEHOP,
	Heap
}

internal enum ForceName
{
	DEP,
	FontDisable,
	ExtensionPoint,
	Heap,
	BlockRemoteImageLoads,
	BlockLowLabel,
	PreferSystem32,
	EnableExportAddressFilter,
	EnableExportAddressFilterPlus,
	EnableImportAddressFilter,
	EnableRopStackPivot,
	EnableRopCallerCheck,
	EnableRopSimExec,
	ForceRelocateImages,
	BottomUp,
	HighEntropy,
	CFG,
	StrictCFG,
	ChildProcess,
	DynamicCode,
	StrictHandle,
	SystemCall,
	DisableFsctlSystem,
	UserShadowStack,
	SEHOP,
	MicrosoftSignedOnly,
	EnforceModuleDependencySigning
}

internal enum PolicyOptionName
{
	Enable,
	EmulateAtlThunks,
	ForceRelocateImages,
	RequireInfo,
	BottomUp,
	HighEntropy,
	DisableWin32kSystemCalls,
	Audit,
	DisableFsctlSystemCalls,
	AuditFsctlSystemCalls,
	DisableExtensionPoints,
	BlockDynamicCode,
	AllowThreadsToOptOut,
	SuppressExports,
	StrictControlFlowGuard,
	MicrosoftSignedOnly,
	AllowStoreSignedBinaries,
	EnforceModuleDependencySigning,
	AuditStoreSigned,
	AuditEnforceModuleDependencySigning,
	DisableNonSystemFonts,
	BlockRemoteImageLoads,
	BlockLowLabelImageLoads,
	PreferSystem32,
	AuditRemoteImageLoads,
	AuditLowLabelImageLoads,
	AuditPreferSystem32,
	EnableExportAddressFilter,
	AuditEnableExportAddressFilter,
	EnableExportAddressFilterPlus,
	AuditEnableExportAddressFilterPlus,
	EnableImportAddressFilter,
	AuditEnableImportAddressFilter,
	EnableRopStackPivot,
	AuditEnableRopStackPivot,
	EnableRopCallerCheck,
	AuditEnableRopCallerCheck,
	EnableRopSimExec,
	AuditEnableRopSimExec,
	TelemetryOnly,
	TerminateOnError,
	DisallowChildProcessCreation,
	AuditChildProcess,
	UserShadowStack,
	UserShadowStackStrictMode,
	AuditUserShadowStack,
	SetContextIpValidation,
	AuditSetContextIpValidation,
	BlockNonCetBinaries,
	BlockNonCetBinariesNonEhcont,
	AuditBlockNonCetBinaries
}


internal static partial class SetProcessMitigationsCommand
{
	private static readonly MitigationOptions[] BlockInSystemMode =
	[
		MitigationOptions.EnableExportAddressFilter,
		MitigationOptions.AuditEnableExportAddressFilter,
		MitigationOptions.EnableExportAddressFilterPlus,
		MitigationOptions.AuditEnableExportAddressFilterPlus,
		MitigationOptions.EnableImportAddressFilter,
		MitigationOptions.AuditEnableImportAddressFilter,
		MitigationOptions.EnableRopStackPivot,
		MitigationOptions.AuditEnableRopStackPivot,
		MitigationOptions.EnableRopCallerCheck,
		MitigationOptions.AuditEnableRopCallerCheck,
		MitigationOptions.EnableRopSimExec,
		MitigationOptions.AuditEnableRopSimExec,
		MitigationOptions.DisallowChildProcessCreation,
		MitigationOptions.AuditChildProcess
	];

	internal static bool IsOptionInList(MitigationOptions[]? list, MitigationOptions option)
	{
		if (list is null)
			return false;
		return list.Contains(option);
	}

	internal static Result setEnableAndDisable(AppMitigations appMitigations,
		MitigationOptions[]? disableList, MitigationOptions[]? enableList, string[]? EAFModulesList, string? isForce, bool isRemove, bool isReset, bool isSystemMode)
	{
		try
		{
			if (enableList?.Length > 0 || disableList?.Length > 0)
			{
				for (int id = 0; id < PolicyPairList.GetCount(); ++id)
				{
					Policy policy = appMitigations.Policies[PolicyPairList.GetPairById(id).PolicyName];
					OPTIONVALUE optionvalue1 = policy.GetPolicy(PolicyPairList.GetPairById(id).PolicyOptionName);
					if (!isSystemMode || !IsOptionInList(BlockInSystemMode, PolicyPairList.GetPairById(id).ParameterName))
					{
						if (IsOptionInList(enableList, PolicyPairList.GetPairById(id).ParameterName) | IsOptionInList(disableList, PolicyPairList.GetPairById(id).ParameterName))
						{
							OPTIONVALUE optionvalue2 = IsOptionInList(enableList, PolicyPairList.GetPairById(id).ParameterName) ? OPTIONVALUE.ON : optionvalue1;
							optionvalue1 = IsOptionInList(disableList, PolicyPairList.GetPairById(id).ParameterName) ? OPTIONVALUE.OFF : optionvalue2;
							if (isReset)
								policy.Flags |= Convert.ToUInt32(PROCESS_MITIGATION_FLAGS.FlagReset, CultureInfo.InvariantCulture);
							if (isRemove)
								policy.Flags |= Convert.ToUInt32(PROCESS_MITIGATION_FLAGS.FlagRemove, CultureInfo.InvariantCulture);
							if (!string.Equals(isForce, "notset", StringComparison.OrdinalIgnoreCase))
								policy.SetForce(PolicyPairList.GetPairById(id).ForceName, string.Equals(isForce, "on", StringComparison.OrdinalIgnoreCase));
						}
						policy.SetPolicy(PolicyPairList.GetPairById(id).PolicyOptionName, optionvalue1);
					}
					else if (IsOptionInList(enableList, PolicyPairList.GetPairById(id).ParameterName) | IsOptionInList(disableList, PolicyPairList.GetPairById(id).ParameterName))
						Logger.Write($"System mode restriction: {PolicyPairList.GetPairById(id).ParameterName} is not supported in system-wide configuration");
				}
			}
			if (EAFModulesList is not null)
			{
				appMitigations.Payload.EAFModules.Clear();
				foreach (string eafModules in EAFModulesList)
				{
					if (!string.IsNullOrEmpty(eafModules) && !appMitigations.Payload.EAFModules.Contains(eafModules))
						appMitigations.Payload.EAFModules.Add(eafModules);
				}
				if (appMitigations.Payload.EnableExportAddressFilterPlus != OPTIONVALUE.ON)
					Logger.Write("Configuration warning: EAF+ modules have been specified, but EAF+ is not currently enabled");
			}
			if (disableList is null && enableList is null && EAFModulesList is null)
				return Result.Success();

			return SecurityPolicyRepository.SaveSecurityConfigurationToRegistry(appMitigations);
		}
		catch (Exception ex)
		{
			return Result.Failure($"Mitigation policy configuration operation failed: {ex.Message}");
		}
	}
}
